package cn.xinfei.xdecision.common.service.enginex.impl;

import cn.hutool.core.date.DateUtil;
import cn.xinfei.xdecision.common.dao.enginex.EngineMapper;
import cn.xinfei.xdecision.common.dao.enginex.EngineVersionMapper;
import cn.xinfei.xdecision.common.model.component.dependency.resp.DecisionComponentDependencyResp;
import cn.xinfei.xdecision.common.model.enginex.po.Engine;
import cn.xinfei.xdecision.common.model.enginex.po.EngineNode;
import cn.xinfei.xdecision.common.model.enginex.po.EngineVersion;
import cn.xinfei.xdecision.common.model.enginex.vo.EngineCreateReqVo;
import cn.xinfei.xdecision.common.model.enginex.vo.EngineListRespVo;
import cn.xinfei.xdecision.common.model.enginex.vo.EngineUpdateReqVo;
import cn.xinfei.xdecision.common.model.enginex.vo.EngineVersionRespVo;
import cn.xinfei.xdecision.common.model.login.SSOUser;
import cn.xinfei.xdecision.common.model.login.SessionManager;
import cn.xinfei.xdecision.common.service.common.sso.SSOService;
import cn.xinfei.xdecision.common.service.component.dependency.EngineNodeComponentRelService;
import cn.xinfei.xdecision.common.service.component.guiderule.RuleVersionService;
import cn.xinfei.xdecision.common.service.component.scriptrule.RuleScriptVersionService;
import cn.xinfei.xdecision.common.service.enginex.EngineNodeService;
import cn.xinfei.xdecision.common.service.enginex.EngineService;
import cn.xinfei.xdecision.common.service.enginex.EngineVersionService;
import cn.xinfei.xdecision.common.service.utils.CodeGenerateUtil;
import cn.xinfei.xdecision.common.utils.constant.SysConstant;
import cn.xinfei.xdecision.common.utils.enums.enginex.EngineVersionStatusEnum;
import cn.xinfei.xdecision.common.utils.enums.enginex.NodeTypeEnum;
import cn.xinfei.xdecision.common.utils.exception.ApiException;
import cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum;
import cn.xinfei.xdecision.common.utils.util.CollectionUtil;
import cn.xinfei.xdecision.common.utils.util.DateUtils;
import cn.xinfei.xdecision.common.utils.util.StringUtil;
import cn.xinfei.xdecision.common.utils.util.ValidationUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import com.github.pagehelper.page.PageMethod;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static cn.xinfei.xdecision.common.utils.enums.enginex.EngineStatusEnum.DEPLOY;
import static cn.xinfei.xdecision.common.utils.enums.enginex.EngineStatusEnum.UNDEPLOY;
import static cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum.*;
import static cn.xinfei.xdecision.common.utils.exception.util.ApiExceptionUtil.exception;

@Service
@Slf4j
public class EngineServiceImpl extends ServiceImpl<EngineMapper, Engine> implements EngineService {
    @Autowired
    private RuleVersionService ruleVersionService;
    @Autowired
    private RuleScriptVersionService ruleScriptVersionService;
    @Resource
    private EngineVersionService versionService;//引擎版本
    @Autowired
    private EngineMapper engineMapper;
    @Autowired
    private EngineVersionMapper engineVersionMapper;
    @Resource
    private EngineNodeService engineNodeService;
    @Autowired
    private SSOService ssoService;
    @Resource
    private CodeGenerateUtil codeGenerateUtil;

    @Resource
    private EngineNodeComponentRelService engineNodeComponentRelService;

    @Override
    public void uniqueCheck(EngineCreateReqVo engineCreateReqVo) {

        if (!ValidationUtils.isLegalEngineCode(engineCreateReqVo.getEngineCode())) {
            throw exception(ENGINE_CODE_ERROR, "jcl_");
        }
        Engine engine = getEngineByCode(engineCreateReqVo.getEngineCode());
        if (engine != null) {
            throw exception(ENGINE_CODE_EXISTS, engineCreateReqVo.getEngineCode());
        }
        Engine engine1 = getEngineByName(engineCreateReqVo.getEngineName());
        if (engine1 != null) {
            throw exception(ENGINE_NAME_EXISTS, engineCreateReqVo.getEngineName());
        }
    }

    @Override
    public Engine getEngineByCode(String code) {

        QueryWrapper<Engine> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("code", code);
        return this.getOne(queryWrapper);
    }

    @Override
    public Engine getEngineByName(String name) {

        QueryWrapper<Engine> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name", name);
        queryWrapper.eq("organ_id", SessionManager.getLoginAccount().getOrganId());
        return this.getOne(queryWrapper);
    }

    @Transactional
    @Override
    public boolean createEngine(EngineCreateReqVo engineCreateReqVo) {
        Engine engineNew = new Engine();
        if (StringUtil.isBlank(engineCreateReqVo.getEngineCode())) {
            engineCreateReqVo.setEngineCode(codeGenerateUtil.generateYMDCode(SysConstant.JCL_PREFIX, DateUtils.yyyyMMdd));
        }

        uniqueCheck(engineCreateReqVo);

        engineNew.setCode(engineCreateReqVo.getEngineCode());
        engineNew.setName(engineCreateReqVo.getEngineName());
        engineNew.setDescription(engineCreateReqVo.getEngineDescription());
        engineNew.setOrganId(SessionManager.getLoginAccount().getOrganId());
        engineMapper.insert(engineNew);

        // 新增默认版本
        EngineVersion engineVersion = new EngineVersion();
        engineVersion.setStatus(EngineVersionStatusEnum.NEW.getStatus());
        engineVersion.setEngineCode(engineCreateReqVo.getEngineCode());
        engineVersion.setVersionNo(1L);
        engineVersionMapper.insert(engineVersion);

        long versionId = engineVersion.getId();
        //新增默认开始节点
        EngineNode node = new EngineNode();
        node.setNodeX(200d);
        node.setNodeY(400d);
        node.setNodeName(NodeTypeEnum.START.getMessage());
        node.setNodeOrder(1);
        node.setNodeType(NodeTypeEnum.START.getType());
        node.setNodeCode(engineCreateReqVo.getEngineCode() + "_" + node.getNodeOrder());
        node.setVersionId(versionId);
        engineNodeService.saveEngineNode(node);

        //新增默认结束节点
        EngineNode endNode = new EngineNode();
        endNode.setNodeX(400d);
        endNode.setNodeY(400d);
        endNode.setNodeName(NodeTypeEnum.END.getMessage());
        endNode.setNodeOrder(2);
        endNode.setNodeType(NodeTypeEnum.END.getType());
        endNode.setNodeCode(engineCreateReqVo.getEngineCode() + "_" + endNode.getNodeOrder());
        endNode.setVersionId(versionId);
        engineNodeService.saveEngineNode(endNode);
        return true;
    }

    @Override
    public boolean updateEngine(SSOUser ssoUser, EngineUpdateReqVo engineUpdateReqVo) {
        Engine engine = getEngineByCode(engineUpdateReqVo.getEngineCode());
        if (engine == null) {
            throw new ApiException(ENGINE_NOT_EXISTS.getCode(), ENGINE_NOT_EXISTS.getMessage());
        }
        if (StringUtils.isNotEmpty(engineUpdateReqVo.getEngineName())) {
            Engine engine1 = getEngineByName(engineUpdateReqVo.getEngineName());
            if (engine1 != null && !engine1.getCode().equals(engineUpdateReqVo.getEngineCode())) {
                throw exception(ENGINE_NAME_EXISTS, engineUpdateReqVo.getEngineName());
            }
            engine.setName(engineUpdateReqVo.getEngineName());
        }
        engine.setUpdateUserId(ssoUser.getId());
        engine.setUpdatedTime(DateUtil.now());
        engine.setDescription(engineUpdateReqVo.getEngineDescription());
        engineMapper.updateById(engine);
        return true;
    }

    @Override
    public PageInfo<EngineListRespVo> getEngineList(String searchString, Integer pageNo, Integer pageSize) {
        PageMethod.startPage(pageNo, pageSize);
        List<Engine> engines = engineMapper.getEngineListWithVersion(searchString, SessionManager.getLoginAccount().getOrgIdList());
        PageInfo pageInfo = new PageInfo<>(engines);
        if (engines == null || engines.isEmpty()) {
            return new PageInfo<>();
        }
        List<EngineListRespVo> engineListRespVos = new ArrayList<>();
        for (Engine engine : engines) {
            EngineListRespVo engineListRespVo = new EngineListRespVo();
            engineListRespVo.setId(engine.getId());
            engineListRespVo.setEngineName(engine.getName());
            engineListRespVo.setEngineCode(engine.getCode());
            engineListRespVo.setEngineDescription(engine.getDescription());
            engineListRespVo.setUpdatedTime(engine.getUpdatedTime());
            engineListRespVo.setCreatedTime(engine.getCreatedTime());
            engineListRespVo.setCreateUserId(engine.getCreateUserId());
            engineListRespVo.setUpdateUserId(engine.getUpdateUserId());

            String engineRunState = UNDEPLOY.getName();
            Long currentVersionNo = null;
            Long latestVersionNo = null;

            List<EngineVersion> engineVersionList = engine.getEngineVersionList();
            if (CollectionUtil.isNotNullOrEmpty(engineVersionList)) {
                for (EngineVersion engineVersion : engine.getEngineVersionList()) {
                    if (engineVersion.getStatus().equals(DEPLOY.getStatus())) {//有运行中版本，则引擎运行状态为：运行中
                        engineRunState = DEPLOY.getName();
                        currentVersionNo = engineVersion.getVersionNo();
                        engineListRespVo.setCurrentVersionNoDesc("V" + currentVersionNo);
                    }
                    latestVersionNo = (latestVersionNo != null && latestVersionNo >= engineVersion.getVersionNo()) ? latestVersionNo : engineVersion.getVersionNo();
                }
            }
            engineListRespVo.setEngineRunState(engineRunState);
            engineListRespVo.setCurrentVersionNo(currentVersionNo);
            engineListRespVo.setLatestVersionNo(latestVersionNo);
            engineListRespVo.setLatestVersionNoDesc("V" + latestVersionNo);
            engineListRespVos.add(engineListRespVo);
        }

        ssoService.fillUserInfoToVO(engineListRespVos);

        pageInfo.setList(engineListRespVos);
        return pageInfo;
    }

    private Set<String> getFieldEnByEngineVersion(long versionId) {
        EngineVersion engineVersion = versionService.getById(versionId);
        Engine engine = new Engine();
        engine.setCode(engineVersion.getEngineCode());
        Set<String> fieldEns = new HashSet<>();
        if (null != engineVersion) {
            List<EngineNode> engineNodeList = engineNodeService.getEngineNodeListByEngineVersionId(engineVersion.getId());
            for (int i = 0; i < engineNodeList.size(); i++) {
                EngineNode node = engineNodeList.get(i);
                fieldEns.addAll(getFieldEnList(node));
            }
        }
        return fieldEns;
    }

    private Set<String> getFieldEnList(EngineNode node) {
        String nodeType = node.getNodeType();
        Set<String> fieldEns = new HashSet<>();
        if (nodeType == null) {
            return fieldEns;
        }
        NodeTypeEnum nodeTypeEnum = NodeTypeEnum.adapad(nodeType);
        switch (nodeTypeEnum) {
            case START://开始节点，无逻辑

                break;
            case RULE_BASE://规则集 从nodeJson.deny_rules.rules[].userId
                fieldEns.addAll(getRuleFieldEnList(node));
                break;
            case CLASSIFY://分组 nodeJson.fields->{字段对象。fieldcode}
                fieldEns.addAll(getGroupFieldEnList(node));
                break;
            case SCORECARD://评分卡 nodeJson直接取
                fieldEns.addAll(getScorecardFieldEnList(node));
                break;
//            case 5://黑名单 nodeJson 根据nodeId取关联表查询内外部名单库id，
//            case 6://白名单 nodeJson 根据nodeId取关联表查询内外部名单库id，
//                fieldEns.addAll(getListDbFieldEnList(node));
//                break;
            case SANDBOX://分流，无字段

                break;
            case DECISION://决策选项 nodeJson.input[]字段对象
                fieldEns.addAll(getDecisionOptionsFieldEnList(node));
                break;
            case DECISION_FLOW://子引擎 nodeJson中直接为引擎id取运行中版本（t_engine_version中boot_state为1的是运行版本）
                fieldEns.addAll(getChildEngineFieldEnList(node));
                break;
//            case 15://模型 nodeJson取模型id
//                fieldEns.addAll(getModelsFieldEnList(node));
//                break;
            case DECISION_TABLES://决策表 nodeJson取决策表id
                fieldEns.addAll(getDecisionTablesFieldEnList(node));
                break;
            case DECISION_TREE://决策树
                fieldEns.addAll(getDecisionTreeFieldEnList(node));
                break;
            case CREDIT_REPORT://人行征信
                break;
            case AGGREGATION://聚合节点
                break;
            case CHAMPION_CHALLENGE://冠军挑战节点
                break;
        }
        return fieldEns;
    }

    private Set<String> getRuleFieldEnList(EngineNode node) {
        Set<String> fieldEns = new HashSet<>();
        String nodeJson = node.getNodeJson();
        if (nodeJson == null || "".equals(nodeJson)) {
            return fieldEns;
        }
        JSONObject ruleMap = JSON.parseObject(nodeJson);
        Integer groupType = ruleMap.getInteger("groupType");
        List<JSONObject> maps = new ArrayList<>();
        Object o;
        if (groupType == 1) {
            o = JSON.parseObject(ruleMap.get("mutexGroup").toString(), Map.class).get("rules");
            maps = JSON.parseArray(JSON.toJSONString(o), JSONObject.class);
        } else if (groupType == 2) {
            o = JSON.parseObject(ruleMap.get("executeGroup").toString(), Map.class).get("rules");
            maps = JSON.parseArray(JSON.toJSONString(o), JSONObject.class);
        } else {
            return fieldEns;
        }


        for (JSONObject map : maps) {
            Long id = map.getLong("userId");
            Long ruleVersionId = map.getLong("ruleVersionId");
            int difficulty = map.getIntValue("difficulty");
            switch (difficulty) {
                case 2:
                    fieldEns.addAll(ruleVersionService.queryFieldEnByVersionId(ruleVersionId));
                    break;
                case 3:
                    fieldEns.addAll(ruleScriptVersionService.queryFieldEnByVersionId(ruleVersionId));
                    break;
            }
        }
        return fieldEns;
    }

    private Set<String> getGroupFieldEnList(EngineNode node) {
        Set<String> fieldEns = new HashSet<>();
        String nodeJson = node.getNodeJson();
        if (nodeJson == null || "".equals(nodeJson)) {
            return fieldEns;
        }
        Map groupMap = JSON.parseObject(nodeJson, Map.class);
        List<Map> maps = JSON.parseArray(JSON.toJSONString(groupMap.get("fields")), Map.class);
        if (maps == null || maps.size() == 0) {
            return fieldEns;
        }
        for (Map map : maps) {
            Object fieldCode = map.get("fieldCode");
            if (fieldCode != null) {
                fieldEns.add(fieldCode.toString());
            }
        }
        return fieldEns;
    }

    private Set<String> getScorecardFieldEnList(EngineNode node) {
        String nodeJson = node.getNodeJson();
        Set<String> fieldEns = new HashSet<>();
        if (StringUtils.isBlank(nodeJson)) {
            return fieldEns;
        }
//        List<Long> versionIds = EngineNodeJsonUtil.getExecuteIdList(node, "versionId");
//        if (versionIds==null||versionIds.isEmpty()){
//            return fieldEns;
//        }
//        for (Long versionId : versionIds) {
//            if (versionId!=null&&versionId>0){
//                fieldEns.addAll(scorecardVersionService.queryFieldEnByVersionId(versionId));
//            }
//        }
        return fieldEns;
    }

    private Set<String> getListDbFieldEnList(EngineNode node) {
        String nodeJson = node.getNodeJson();
        Set<String> fieldEns = new HashSet<>();
        if (StringUtils.isBlank(nodeJson)) {
            return fieldEns;
        }
//        List<Long> listDbIds = EngineNodeJsonUtil.getExecuteIdList(node, "listDbId");
//        for (Long listDbId : listDbIds) {
//            fieldEns.addAll(listDbService.queryFieldEnsByListDbIds(listDbId));
//        }
        return fieldEns;
    }

    private Set<String> getDecisionOptionsFieldEnList(EngineNode node) {
        Set<String> fieldEns = new HashSet<>();
        String nodeJson = node.getNodeJson();
        if (nodeJson == null || "".equals(nodeJson)) {
            return fieldEns;
        }
        Map groupMap = JSON.parseObject(nodeJson, Map.class);
        List<JSONObject> maps = JSON.parseArray(JSON.toJSONString(groupMap.get("input")), JSONObject.class);
        for (JSONObject map : maps) {
            long nodeType = map.getLongValue("nodeType");
            Object fieldCode = map.getString("field_code");
            if (nodeType < 0 && fieldCode != null) {
                fieldEns.add(fieldCode.toString());
            }
        }
        return fieldEns;
    }

    private Set<String> getChildEngineFieldEnList(EngineNode node) {
        Set<String> fieldEns = new HashSet<>();
        String nodeJson = node.getNodeJson();
        if (nodeJson == null || "".equals(nodeJson)) {
            return fieldEns;
        }
        String[] split = nodeJson.split(",");
        for (String s : split) {
            List<EngineVersionRespVo> versionList = versionService.getEngineVersionListByEngineCode(s);
            for (EngineVersionRespVo version : versionList) {
                Integer bootState = version.getStatus();
                if (bootState == null || bootState == 0) {
                    continue;
                } else if (bootState == 1) {
                    fieldEns.addAll(this.getFieldEnByEngineVersion(version.getId()));
                }
            }
        }
        return fieldEns;
    }

    private Set<String> getModelsFieldEnList(EngineNode node) {

        Set<String> fieldEns = new HashSet<>();
//        List<Long> versionIds = EngineNodeJsonUtil.getExecuteIdList(node, "modelId");
//        if (versionIds==null||versionIds.isEmpty()){
//            return fieldEns;
//        }
//        for (Long versionId : versionIds) {
////            fieldEns.addAll(modelsService.queryFieldEnByModelsId(versionId.intValue()));
//        }
        return fieldEns;
    }

    private Set<String> getDecisionTablesFieldEnList(EngineNode node) {
        Set<String> fieldEns = new HashSet<>();
//        List<Long> versionIds = EngineNodeJsonUtil.getExecuteIdList(node, "versionId");
//        if (versionIds==null||versionIds.isEmpty()){
//            return fieldEns;
//        }
//        for (Long versionId : versionIds) {
//            fieldEns.addAll(decisionTablesService.queryFieldEnByDecisionTablesVersionId(versionId));
//        }
        return fieldEns;
    }

    private Set<String> getDecisionTreeFieldEnList(EngineNode node) {
        Set<String> fieldEns = new HashSet<>();
//        List<String> versionIds = EngineNodeJsonUtil.getExecuteIdList(node, "code");
//        if (versionIds==null||versionIds.isEmpty()){
//            return fieldEns;
//        }
//        for (Long versionId : versionIds) {
//            fieldEns.addAll(decisionTreeService.queryFieldEnByVersionId(versionId));
//        }
        return fieldEns;
    }

    @Override
    public List<EngineListRespVo> getSonEngineList(String searchCon, Long nodeId) {
        EngineNode selfNode = engineNodeService.getNodeById(nodeId);
        if (Objects.isNull(selfNode)) {
            log.error("找不到当前节点");
            throw new ApiException(ErrorCodeEnum.PARAMS_EXCEPTION);
        }
        EngineVersion engineVersion = versionService.getById(selfNode.getVersionId());
        if (Objects.isNull(engineVersion)) {
            log.error("找不到当前节点对应的引擎");
            throw new ApiException(ErrorCodeEnum.PARAMS_EXCEPTION);
        }

        //排除自己
        Set<String> excludeEngineCodes = Sets.newHashSet(engineVersion.getEngineCode());
        //递归
        recursionEngines(excludeEngineCodes, Sets.newHashSet(engineVersion.getEngineCode()));
        if (StringUtils.isNotEmpty(selfNode.getNodeJson())) {
            excludeEngineCodes.remove(selfNode.getNodeJson());
        }
        //排除
        List<Engine> engines = engineMapper.getEngineListWithVersionStatus(
                SessionManager.getLoginAccount().getOrgIdList(),
                searchCon,
                DEPLOY.getStatus(),
                excludeEngineCodes);
        if (CollectionUtils.isEmpty(engines)) {
            return new ArrayList<>();
        }
        return engines.stream().map(x ->
                new EngineListRespVo()
                        .setId(x.getId())
                        .setEngineCode(x.getCode())
                        .setEngineName(x.getName())).collect(Collectors.toList());
    }

    /**
     * 递归子引擎
     *
     * @param excludeEngineCodes 排除的子引擎
     * @param engineCodes        需要递归查询的子引擎
     */
    private void recursionEngines(Set<String> excludeEngineCodes, Set<String> engineCodes) {
        if (CollectionUtils.isEmpty(engineCodes)) {
            return;
        }
        List<DecisionComponentDependencyResp> dependencyRespList = engineNodeComponentRelService
                .listByEngineNodeCodes(new ArrayList<>(engineCodes), NodeTypeEnum.CHILD_ENGINE.getCode()
                        , EngineVersionStatusEnum.DISABLE.getStatus());
        if (CollectionUtils.isEmpty(dependencyRespList)) {
            return;
        }
        //子引擎
        Set<String> engineCodeList = dependencyRespList.stream()
                .map(dependencyResp -> dependencyResp.getEngineNodeCode()
                        .substring(0, dependencyResp.getEngineNodeCode().lastIndexOf(SysConstant.UNDERLINE))).collect(Collectors.toSet());
        //排除已经排除的子引擎code
        engineCodeList.removeAll(excludeEngineCodes);
        excludeEngineCodes.addAll(engineCodeList);
        recursionEngines(excludeEngineCodes, engineCodeList);
    }
}
